
#include <BPainter>
#include <BStyle>
#include <BImage>
#include <BFont>
#include <BEvent>
#include <BMouseEvent>
#include <BKeyEvent>
#include <BStyle>

#include <BButton>
#include "member_BButton.h"

using namespace BWE;

#define member					(*(member_BButton*)_ptr)
#define member_allocate()		_ptr = new member_BButton(this)
#define member_release()		delete (member_BButton*)_ptr

BButton::BButton(BWidget* parent) : BWidget(parent)
{
	member_allocate();
}
BButton::BButton(Form form, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	member.alignment.ia_form = form;
}
BButton::BButton(const BString& text, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	member.text = text;
}
BButton::BButton(Graph graph, const BString& text, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	member.graph = graph;
	member.text = text;
}
BButton::BButton(BIcon* icon, const BString& text, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	member.icon = icon;
	member.text = text;
}
BButton::BButton(BImage* image, const BString& text, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	member.image = image;
	member.text = text;
}
BButton::BButton(Graph graph, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	member.graph = graph;
}
BButton::BButton(Graph graph, const BColor& color, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	member.graph = graph;
	member.color = color;
}
BButton::BButton(BIcon* icon, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	member.icon = icon;
}
BButton::BButton(BImage* image, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	member.image = image;
}
BButton::BButton(BAction* action, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	setAction(action);
}
BButton::BButton(BMenu* menu, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	setMenu(menu);
}
BButton::~BButton()
{
	member_release();
}

void BButton::setAlign(Align align)
{
	if (member.alignment.ia_align != align)
	{
		member.alignment.ia_align = align;
		member.dirty = true;
		this->adjust();
		this->fresh();
	}
}
Align BButton::align() const
{
	return member.alignment.ia_align;
}

void BButton::setSpacing(int spacing)
{
	if (member.alignment.ia_spacing < 0)
		member.alignment.ia_spacing = 0;
	if (member.alignment.ia_spacing > 1000)
		member.alignment.ia_spacing = 1000;
	if (member.alignment.ia_spacing != spacing)
	{
		member.alignment.ia_spacing = spacing;
		member.dirty = true;
		this->adjust();
		this->fresh();
	}
}
int BButton::spacing() const
{
	return member.alignment.ia_spacing;
}

void BButton::setForm(Form form)
{
	if (member.alignment.ia_form != form)
	{
		member.alignment.ia_form = form;
		member.dirty = true;
		this->adjust();
		this->fresh();
	}
}
Form BButton::form() const
{
	return member.alignment.ia_form;
}

void BButton::setText(const BString& text)
{
	if(member.text != text)
	{
		member.text = text;
		member.dirty = true;
		this->adjust();
		this->fresh();
	}
}
const BString& BButton::text() const
{
	return member.text;
}

void BButton::setText(const BString& text, bool checked)
{
	if (checked)
	{
		if (member.checkedText == text)
			return;
		member.checkedText = text;
	}
	else
	{
		if (member.uncheckText == text)
			return;
		member.uncheckText = text;
	}
	if (member.checkable)
	{
		member.dirty = true;
		this->adjust();
		this->fresh();
	}
}
const BString& BButton::text(bool checked) const
{
	if (checked)
	{
		return member.checkedText;
	}
	else
	{
		return member.uncheckText;
	}
}

void BButton::setColor(BByte r, BByte g, BByte b, BByte a)
{
	setColor(BColor(r, g, b, a));
}
void BButton::setColor(const BColor& color)
{
	if (member.color != color)
	{
		member.color = color;
		this->fresh();
	}
}
const BColor& BButton::color() const
{
	return member.color;
}

void BButton::setGraph(Graph graph)
{
	if (member.graph != graph)
	{
		member.graph = graph;
		member.dirty = true;
		this->fresh();
	}
}
Graph BButton::graph() const
{
	return member.graph;
}

void BButton::setIcon(BIcon* icon)
{
	if (member.icon != icon)
	{
		member.icon = icon;
		member.dirty = true;
		this->adjust();
	}
	this->fresh();
}
const BIcon* BButton::icon() const
{
	return member.icon;
}
BIcon* BButton::icon()
{
	return member.icon;
}

void BButton::setIconSize(int iconWidth, int iconHeight)
{
	setIconSize(BSize(iconWidth, iconHeight));
}
void BButton::setIconSize(const BSize& iconSize)
{
	if (member.iconSize != iconSize)
	{
		member.iconSize = iconSize;
		member.dirty = true;
		this->fresh();
		this->fresh();
	}
}
const BSize& BButton::iconSize() const
{
	return member.iconSize;
}

void BButton::setImage(BImage* image)
{
	if (member.image != image)
	{
		member.image = image;
		member.dirty = true;
	}
	this->fresh();
}
BImage* BButton::image() const
{
	return member.image;
}

void BButton::setAction(BAction* action)
{
	if (member.action != action)
	{
		if (member.action)
		{
			member.disconnect(member.action);
		}
		member.action = action;
		if (member.action)
		{
			if (member.menu)
				this->setMenu(0);
			member.connect(action, Signal_Enabled, &member_BButton::slotActionAbleChanged);
			member.connect(action, Signal_IconChanged, &member_BButton::slotActionIconChanged);
			member.connect(action, Signal_TextChanged, &member_BButton::slotActionTextChanged);
			member.connect(action, Signal_Checked, &member_BButton::slotActionChecked);
			member.checkable = action->checkable();
			this->setEnabled(action->enabled());
		}
		member.dirty = true;
		this->fresh();
	}
}
BAction* BButton::action() const
{
	return member.action;
}

void BButton::setMenu(BMenu* menu)
{
	if (member.menu != menu)
	{
		if (member.menu)
		{
			member.disconnect(member.menu);
			this->removeChild(member.menu);
		}
		member.menu = menu;
		if (member.menu)
		{
			if (member.action)
				this->setAction(0);
			this->addChild(member.menu);
			member.connect(menu, Signal_Hide, &member_BButton::slotMenuHide);
			member.connect(menu, Signal_Enabled, &member_BButton::slotMenuAbleChanged);
			member.connect(menu, Signal_TitleChanged, &member_BButton::slotMenuTitleChanged);
			member.connect(menu, Signal_Style, &member_BButton::slotMenuStyleChanged);
			this->setEnabled(menu->enabled());
		}
		member.dirty = true;
		this->fresh();
	}
}
BMenu* BButton::menu() const
{
	return member.menu;
}

void BButton::setCheckable(bool checkable)
{
	if (member.checkable != checkable)
	{
		member.checkable = checkable;
		member.checked = false;
		if (member.action)
			member.action->setCheckable(checkable);
		this->fresh();
	}
}
bool BButton::checkable() const
{
	return member.checkable;
}

void BButton::setChecked(bool checked)
{
	if (!member.checkable)
		return;
	if (member.checked != checked)
	{
		member.checked = checked;
		emit(Signal_Checked, checked);
		if (member.action && member.action->enabled() && member.action->checkable())
		{
			member.action->setChecked(checked);
		}
		this->fresh();
	}
}
bool BButton::checked() const
{
	return member.checked;
}

void BButton::click() const
{
	if (this->enabled())
	{
		this->emit(Signal_Clicked, this->centerRect().center());
	}
}

BSize BButton::sizeHint() const
{
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
	BSize csize = member.alignment.ia_hintSize;
	BSize hsize = this->preferSize(csize);
	return hsize;
}

void BButton::resizeEvent(const BEvent& event)
{
	member.dirty = true;
	BWidget::resizeEvent(event);
}

void BButton::styleEvent(const BEvent& event)
{
	BWidget::styleEvent(event);
	const BStyle* style = this->realStyle();
	if (style)
	{
		const BValue& textValue = style->value(Value_Text);
		if (textValue.valid())
			member.text = (BString&)textValue;

		const BValue& formValue = style->value(Value_Form);
		if (formValue.valid())
			member.alignment.ia_form = formValue;

		const BValue& alignValue = style->value(Value_Align);
		if (alignValue.valid())
			member.alignment.ia_align = alignValue;

		const BValue& spaceValue = style->value(Value_Space);
		if (spaceValue.valid())
			member.alignment.ia_spacing = spaceValue;

		const BValue& graphValue = style->value(Value_Graph);
		if (graphValue.valid())
			member.graph = graphValue;

		const BValue& imageValue = style->value(Value_Image);
		if (imageValue.valid())
			member.image = (BImage*)imageValue;

		const BValue& iconSizeValue = style->value(Value_Icon_Size);
		if (iconSizeValue.valid())
			member.iconSize = iconSizeValue;

		const BValue& iconValue = style->value(Value_Icon);
		if (iconValue.valid())
			member.icon = (BIcon*)iconValue;
	}
	member.dirty = true;
	this->adjust();
}
void BButton::freshEvent(const BEvent& event)
{
	BWidget::styleEvent(event);
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
}
void BButton::paintEvent(const BEvent& event)
{
	BPainter painter(this);

	StyleRole textColorRole = Color_Text;
	if (!this->enabled())
		textColorRole = Color_Text_Disable;
	else if (member.state == State_Pressed)
		textColorRole = Color_Text_Pressed;
	else if (member.state == State_Hovered)
		textColorRole = Color_Text_Hovered;
	else if (member.checked)
		textColorRole = Color_Text_Checked;
	else if (this->actived())
		textColorRole = Color_Text_Actived;

	State state = member.state;
	painter.drawBackground(state, member.checked);
	painter.drawBorder(state, member.checked);
	painter.drawPerch(state, member.checked);
	painter.drawMargin(state, member.checked);

	BRect crect = this->centerRect();
	if (member.alignment.ia_imageRect.valid())
	{
		BRect imageRect = member.alignment.ia_imageRect;
		if (member.iconSize.empty())
			imageRect = crect.wrap(imageRect);
		BImage* image = member.image;
		if (member.icon)
		{
			image = member.icon->image(state, member.checked);
		}
		if (!image && member.action)
		{
			if (BIcon* icon = member.action->icon())
				image = icon->image(state, member.checked);
		}
		if (!image && member.menu)
		{
			if (BIcon* icon = member.menu->titleIcon())
				image = icon->image(state, member.checked);
		}
		if (!painter.setColor(member.color))
		{
			painter.setColor(textColorRole);
		}
		if (image)
		{
			painter.drawImage(imageRect, image, member.iconSize.empty());
		}
		else if (member.graph)
		{
			painter.drawGraph(imageRect, member.graph);
		}
	}

	if (member.alignment.ia_textRect.valid())
	{
		bool drawn = false;
		painter.setColor(textColorRole);
		if (member.checkable)
		{
			if (member.checked && member.checkedText.size())
			{
				if (member.t_text_checked.size())
					painter.drawText(member.alignment.ia_textRect, member.t_text_checked, member.alignment.ia_align);
				else
					painter.drawText(member.alignment.ia_textRect, member.checkedText, member.alignment.ia_align);
				drawn = true;
			}
			if (!member.checked && member.uncheckText.size())
			{
				if (member.t_text_uncheck.size())
					painter.drawText(member.alignment.ia_textRect, member.t_text_uncheck, member.alignment.ia_align);
				else
					painter.drawText(member.alignment.ia_textRect, member.uncheckText, member.alignment.ia_align);
				drawn = true;
			}
		}
		if (!drawn)
		{
			if (member.t_text.size())
				painter.drawText(member.alignment.ia_textRect, member.t_text, member.alignment.ia_align);
			else if (member.text.size())
				painter.drawText(member.alignment.ia_textRect, member.text, member.alignment.ia_align);
			else if (member.action)
				painter.drawText(member.alignment.ia_textRect, member.action->text(), member.alignment.ia_align);
		}
	}
}

void BButton::keyDownEvent(const BKeyEvent& keyEvent)
{
	if (keyEvent.key() == Key_Enter)
	{
		this->click();
	}
	BWidget::keyDownEvent(keyEvent);
}
void BButton::keyUpEvent(const BKeyEvent& keyEvent)
{
	BWidget::keyUpEvent(keyEvent);
}

void BButton::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	if (member.state != State_Hovered)
	{
		member.state = State_Hovered;
		this->fresh();
	}
}
void BButton::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	if (member.state != State_Normal)
	{
		member.state = State_Normal;
		this->fresh();
	}
}

void BButton::mousePressEvent(const BMouseEvent& mouseEvent)
{
	if (member.state != State_Pressed)
	{
		member.state = State_Pressed;
		this->fresh();
	}
}
void BButton::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	if (member.state != State_Hovered)
	{
		member.state = State_Hovered;
		this->fresh();
	}
}

void BButton::mouseClickEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	if (mouseEvent.button() == Button_Left)
	{
		if (member.checkable)
		{
			setChecked(!member.checked);
			emit(Signal_Toggled, member.checked);
		}
		if (member.action && member.action->enabled())
		{
			member.action->trigger();
		}
		if (member.menu && member.menu->enabled())
		{
			BMenu* menu = member.menu;
			if (menu->visible())
			{
				menu->hide();
			}
			else if (menu->enabled())
			{
				BSize size = menu->sizeHint();
				BRect rootRect = this->mapFrom(this->root(), this->root()->size());
				BRect rect = this->size();
				{//Orientation_Horizontal
					int heightDown = rootRect.bottom() - rect.bottom();
					if (heightDown < size.height())
					{
						menu->setPos(rect.left(), rect.top() - size.height());
					}
					else
					{
						menu->setPos(rect.left(), rect.bottom());
					}
					if (size.width() > rootRect.width())
						size.width() = rootRect.width();
					menu->setSize(size);
					menu->setParent(this);
					menu->popup();
				}
			}
		}
	}
	BWidget::mouseClickEvent(mouseEvent);
}
